package com.baomibing.flow.service.impl;

import com.baomibing.core.common.Assert;
import com.baomibing.core.common.SearchResult;
import com.baomibing.core.exception.ExceptionEnum;
import com.baomibing.core.exception.ServerRuntimeException;
import com.baomibing.core.process.StateWorkFlow;
import com.baomibing.flow.action.FlowAction;
import com.baomibing.flow.dto.SysFlowDto;
import com.baomibing.flow.entity.SysFlow;
import com.baomibing.flow.exception.FlowExceptionEnum;
import com.baomibing.flow.mapper.SysFlowMapper;
import com.baomibing.flow.service.SysFlowService;
import com.baomibing.flow.state.FlowState;
import com.baomibing.orm.base.MBaseServiceImpl;
import com.baomibing.tool.util.Checker;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * SysFlowServiceImpl
 *
 * @author frog 2023/10/16 10:19
 * @version 1.0.0
 **/
@Service
public class SysFlowServiceImpl extends MBaseServiceImpl<SysFlowMapper, SysFlow, SysFlowDto> implements SysFlowService {

    @Override
    public SearchResult<SysFlowDto> search(SysFlowDto flow, int pageNumber, int pageSize) {
        LambdaQueryWrapper<SysFlow> wrapper = lambdaQuery().like(Checker.beNotEmpty(flow.getName()), SysFlow::getName, flow.getName()).orderByDesc(SysFlow::getUpdateTime);
        return search(wrapper, pageNumber, pageSize);
    }

    @Override
    public SearchResult<SysFlowDto> searchAllActive(SysFlowDto flow, int pageNumber, int pageSize) {
        LambdaQueryWrapper<SysFlow> wrapper = lambdaQuery().like(Checker.beNotEmpty(flow.getName()), SysFlow::getName, flow.getName()).orderByDesc(SysFlow::getUpdateTime).eq(SysFlow::getState, FlowState.ACTIVE.name());
        return search(wrapper, pageNumber, pageSize);
    }

    private List<SysFlowDto> listByNameAndVersion(String name, String version) {
        return mapper(baseMapper.selectList(lambdaQuery().eq(SysFlow::getName, name).eq(SysFlow::getFlowVersion, version)));
    }

    @Override
    public void saveFlow(SysFlowDto flow) {
        flow.setFlowVersion("1.0");
        StateWorkFlow.doInitState(flow);
        List<SysFlowDto> list = listByNameAndVersion(flow.getName(), flow.getFlowVersion());
        if (Checker.beNotEmpty(list)) {
            throw new ServerRuntimeException(FlowExceptionEnum.FLOW_NAME_AND_VERSION_DUPLICATE);
        }
        saveIt(flow);
    }

    @Override
    public void updateFlow(SysFlowDto flow) {
        List<SysFlowDto> list = listByNameAndVersion(flow.getName(), flow.getFlowVersion());
        Set<String> ids = list.stream().map(SysFlowDto::getId).filter(id -> !id.equals(flow.getId())).collect(Collectors.toSet());
        if (Checker.beNotEmpty(ids)) {
            throw new ServerRuntimeException(FlowExceptionEnum.FLOW_NAME_AND_VERSION_DUPLICATE);
        }
        updateIt(flow);
    }

    private void updateStateByIds(Set<String> ids, FlowAction action) {
        Assert.CheckArgument(ids);
        Assert.CheckArgument(action);
        List<SysFlowDto> flows = super.gets(ids);
        if (flows.size() != ids.size()) {
            throw new ServerRuntimeException(ExceptionEnum.IDS_CONTAINS_INVALID_ID);
        }
        for (SysFlowDto flow : flows) {
            assertBeLock(flow);
            StateWorkFlow.doProcess(flow, action);
        }
        super.updateItBatch(flows);
    }

    @Override
    public void activeFlows(Set<String> ids) {
        updateStateByIds(ids, FlowAction.USE);
    }

    @Override
    public void stopFlows(Set<String> ids) {
        updateStateByIds(ids, FlowAction.STOP);
    }

    @Override
    public SysFlowDto getFlow(String id) {
        if (Checker.beEmpty(id)) {
            return null;
        }
        return getIt(id);
    }

    @Override
    public void doCopy(SysFlowDto flow) {
        Assert.CheckArgument(flow);
        Assert.CheckArgument(flow.getId());
        SysFlowDto dbFlow = getIt(flow.getId());
        flow.setFlowVersion(new BigDecimal(dbFlow.getFlowVersion()).add(BigDecimal.ONE).toPlainString());
        flow.setId(null);
        saveIt(flow);
    }

    @Override
    public void deleteFlows(Set<String> ids) {
        List<SysFlowDto> SysFlowDto = super.gets(Sets.newHashSet(ids));
        for (SysFlowDto flow : SysFlowDto) {
            assertBeLock(flow);
            StateWorkFlow.doAssertDelete(flow);
        }
        super.deletes(Sets.newHashSet(ids));
    }
}
